package it.softphone.commons.service.security.shiro;


import it.softphone.commons.service.security.SecurityAccountService;

import java.io.IOException;
import java.security.GeneralSecurityException;

import javax.security.auth.login.AccountLockedException;
import javax.security.auth.login.AccountNotFoundException;
import javax.security.auth.login.CredentialNotFoundException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;

/**
 * 
 * @author softphone
 *
 */
public class ShiroSecurityAccountService implements SecurityAccountService, Filter {
	private final Filter filter;
	
	protected ShiroSecurityAccountService(Filter filter) {
		super();
		this.filter = filter;
	}

	Subject getSubject() {
		return SecurityUtils.getSubject();
	}
	
	public String getId() {
		final Subject subject = getSubject();
		return subject.getPrincipal().toString();
	}


	public boolean isAuthenticated() {
		final Subject subject = getSubject();
		return subject.isAuthenticated();
	}

	public boolean hasRole(String role) {
		if( role==null ) throw new IllegalArgumentException( "role is null");
		return getSubject().hasRole(role);
	}

	public boolean[] hasRoles(java.util.Collection<String> roles) {
		if( roles==null ) throw new IllegalArgumentException( "roles is null");
		return getSubject().hasRoles( new java.util.ArrayList<String>(roles) );
	}

	public boolean hasAllRoles(java.util.Collection<String> roles) {
		if( roles==null ) throw new IllegalArgumentException( "roles is null");
		return getSubject().hasAllRoles(roles);
	}

	public boolean isPermitted(String permission) {
		if( permission==null ) throw new IllegalArgumentException( "permission is null");
		return getSubject().isPermitted(permission);
	}

	public boolean[] isPermitted(java.util.Collection<String> permissions) {
		if( permissions==null ) throw new IllegalArgumentException( "permissions is null");
		return getSubject().isPermitted( permissions.toArray( new String[ permissions.size() ]));
	}

	public boolean isPermittedAll(java.util.Collection<String> permissions) {
		if( permissions==null ) throw new IllegalArgumentException( "permissions is null");
		return getSubject().isPermittedAll( permissions.toArray( new String[ permissions.size() ]));
	}

	public void login(String username, String password, boolean rememberMe) throws GeneralSecurityException, AccountNotFoundException, CredentialNotFoundException, AccountLockedException {
		final Subject subject = getSubject();

		UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        token.setRememberMe(rememberMe);
		
        try {
        	
        	subject.login(token);
        	
        } catch (UnknownAccountException uae) {
        	throw new AccountNotFoundException( uae.getMessage() );
        } catch (IncorrectCredentialsException ice) {
        	throw new CredentialNotFoundException( ice.getMessage() );
        } catch (LockedAccountException lae) {
        	throw new AccountLockedException( lae.getMessage() );
        }
        catch (AuthenticationException ae) {
        	throw new GeneralSecurityException(ae);
        }
        
	}

	public boolean logout() {
		final Subject subject = getSubject();
		try {
			subject.logout();
			return true;
		}
		catch( Exception e  ) {
			// TODO log
		}
		return false;
	}

	public void destroy() {
		if( filter!=null) filter.destroy();
		
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		if( filter!=null) {
		
			filter.doFilter(request, response, chain);
		}
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		
		if( filter!=null) {
			ThreadContext.put( "javax.servlet.ServletContext", filterConfig.getServletContext());
			
			filter.init(filterConfig);

			ThreadContext.remove("javax.servlet.ServletContext");
		}
		
	}

	public <T> T getPrincipal(Class<T> type) {
		if( type==null ) return null;

		final Subject subject = getSubject();

		PrincipalCollection pc = subject.getPrincipals();
		
		if( pc==null ) return null;
		
		return pc.oneByType(type);
	}

}
